package com.fc.advbox.common;

import com.fc.advbox.common.convert.ObjectConverter;
import com.fc.advbox.utils.SpringContextUtils;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.core.Converter;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.slf4j.Logger;

import static com.fc.advbox.utils.ClassUtils.getElementType;
import static com.fc.advbox.utils.ClassUtils.isPrimitive;

/**
 * Created by futurecreative on 2017/7/19.
 */
public class ConverterUtil {

    /**
     * The Constant LOGGER.
     */
    private static final Logger log = LoggerFactory.getLogger(ConverterUtil.class);

    /**
     * the beanCopierMap
     */
    private static final ConcurrentMap<String, BeanCopier> beanCopierMap = new ConcurrentHashMap<>();

    /**
     * The Constant CACHED_CUSTOM_CONVERTER_MAP.
     */
    private static final Map<String, ObjectConverter> CACHED_CUSTOM_CONVERTER_MAP = new ConcurrentHashMap<String, ObjectConverter>();

    /**
     * Instantiates a new converter util.
     */
    private ConverterUtil() {
    }

    public static <T, F> List<F> convertList(Class<F> target, List<T> sourceList){
        return convertList(target, sourceList, null);
    }

    /**
     * Convert list.
     *
     * @param <T>                  the generic type
     * @param <F>                  the generic type
     * @param target               the target
     * @param sourceList           the source list
     * @param customConverterClass the custom converter class
     * @return the list
     */
    public static <T, F> List<F> convertList(Class<F> target, List<T> sourceList,
                                             Class<? extends ObjectConverter> customConverterClass) {
        if (CollectionUtils.isNotEmpty(sourceList)) {
            @SuppressWarnings("unchecked")
            List<F> targetList = new ArrayList();
            for (T t : sourceList) {
                try {
                    targetList.add(convert(t, target, customConverterClass));
                } catch (Exception e) {
                    log.error("When copy instance" + t, e);
                }
            }
            return targetList;
        } else {
            return null;// NOSONAR
        }

    }

    public static <T> T convert(Object source, Class<T> target) {
        return convert(source, target, null);
    }

    /**
     * @description 两个类对象之间转换
     * @param source src
     * @param target tgt
     * @return na
     */
    public static <T> T convert(Object source, Class<T> target, Class<? extends ObjectConverter> customConverterClass) {
        T ret = null;
        if (source != null) {
            try {
                ret = target.newInstance();
            } catch (ReflectiveOperationException e) {
                throw new RuntimeException("create class[" + target.getName()
                        + "] instance error", e);
            }
            BeanCopier beanCopier = getBeanCopier(source.getClass(), target);
            beanCopier.copy(source, ret, new DeepCopyConverter(target));

            ObjectConverter customConverter = getCustomConverterInstance(customConverterClass);
            if (customConverter != null) {
                if (ret.getClass().getName().endsWith("Entity")) {
                    customConverter.convertFromDto(source, ret);
                } else if (ret.getClass().getName().endsWith("Dto")) {
                    customConverter.convertToDto(source, ret);
                }
            }
        }
        return ret;
    }

    /**
     * Gets the custom converter instance.
     *
     * @param customConverterClass the custom converter class
     * @return the custom converter instance
     */
    private static ObjectConverter getCustomConverterInstance(
            Class<? extends ObjectConverter> customConverterClass) {
        if (customConverterClass == null) {
            return null;// NOSONAR
        }
        String key = customConverterClass.getName();
        ObjectConverter converter = CACHED_CUSTOM_CONVERTER_MAP.get(key);
        if (converter == null) {
            synchronized (CACHED_CUSTOM_CONVERTER_MAP) {
                try {
                    converter = SpringContextUtils.getBean(customConverterClass);
                } catch (BeansException e) {// NOSONAR
                    log.info(customConverterClass.getName() + " is not a component, need new instance.");// NOSONAR
                }
                if (converter == null) {
                    try {
                        converter = customConverterClass.newInstance();
                        CACHED_CUSTOM_CONVERTER_MAP.put(key, converter);
                    } catch (InstantiationException e) {
                        log.info(e.getMessage(), e);
                        return null;
                    } catch (IllegalAccessException e) {
                        log.info(e.getMessage(), e);
                        return null;
                    }
                }
            }
        }
        return converter;
    }

    public static class DeepCopyConverter implements Converter {

        /**
         * The Target.
         */
        private Class<?> target;

        /**
         * Instantiates a new Deep copy converter.
         *
         * @param target
         *            the target
         */
        public DeepCopyConverter(Class<?> target) {
            this.target = target;
        }

        @Override
        public Object convert(Object value, Class targetClazz, Object methodName) {
            if (value instanceof List) {
                List values = (List) value;
                List retList = new ArrayList<>(values.size());
                for (final Object source : values) {
                    String tempFieldName = methodName.toString().replace("set",
                            "");
                    String fieldName = tempFieldName.substring(0, 1)
                            .toLowerCase() + tempFieldName.substring(1);
                    Class clazz = getElementType(target, fieldName);
                    if(clazz != null){
                        retList.add(ConverterUtil.convert(source, clazz, null));
                    }
                }
                return retList;
            } else if (value instanceof Map) {
                // TODO 暂时用不到，后续有需要再补充
            } else if (!isPrimitive(targetClazz)) {
                return ConverterUtil.convert(value, targetClazz,null);
            }
            return value;
        }
    }

    /**
     * @description 获取BeanCopier
     * @param source
     * @param target
     * @return
     * @return BeanCopier
     */
    public static BeanCopier getBeanCopier(Class<?> source, Class<?> target) {
        String beanCopierKey = generateBeanKey(source, target);
        if (beanCopierMap.containsKey(beanCopierKey)) {
            return beanCopierMap.get(beanCopierKey);
        } else {
            BeanCopier beanCopier = BeanCopier.create(source, target, true);
            beanCopierMap.putIfAbsent(beanCopierKey, beanCopier);
        }
        return beanCopierMap.get(beanCopierKey);
    }

    /**
     * @description 生成两个类的key
     * @param source
     * @param target
     * @return
     * @return String
     */
    public static String generateBeanKey(Class<?> source, Class<?> target) {
        return source.getName() + "@" + target.getName();
    }
}
